<!-- eslint-disable -->
<template>
  <div
    :class="[{
      'app-table--border': border
    }]"
    class="app-table">
    <slot name="topbar" />

    <el-table
      ref="elTable"
      :data="internalTableData"
      :height="height"
      :max-height="maxHeight"
      :stripe="stripe"
      :border="border"
      :size="size"
      :fit="fit"
      :show-header="showHeader"
      :highlight-current-row="highlightCurrentRow"
      :current-row-key="currentRowKey"
      :row-class-name="rowClassName"
      :row-style="rowStyle"
      :cell-class-name="cellClassName"
      :cell-style="cellStyle"
      :header-row-class-name="headerRowClassName"
      :header-row-style="headerRowStyle"
      :header-cell-class-name="headerCellClassName"
      :header-cell-style="headerCellStyle"
      :row-key="rowKey"
      :empty-text="internalEmptyText"
      :default-expand-all="defaultExpandAll"
      :expand-row-keys="expandRowKeys"
      :default-sort="defaultSort"
      :tooltip-effect="tooltipEffect"
      :show-summary="showSummary"
      :sum-text="sumText"
      :summary-method="summaryMethod"
      :span-method="spanMethod"
      :select-on-indeterminate="selectOnIndeterminate"
      @select="(selection, row) => emit('select', selection, row)"
      @select-all="selection => emit('select-all', selection)"
      @selection-change="selection => emit('selection-change', selection)"
      @cell-mouse-enter="(row, column, cell, event) => emit('cell-mouse-enter', row, column, cell, event)"
      @cell-mouse-leave="(row, column, cell, event) => emit('cell-mouse-leave', row, column, cell, event)"
      @cell-click="(row, column, cell, event) => emit('cell-click', row, column, cell, event)"
      @cell-dblclick="(row, column, cell, event) => emit('cell-dblclick', row, column, cell, event)"
      @row-click="(row, event, column) => emit('row-click', row, event, column)"
      @row-contextmenu="(row, event) => emit('row-contextmenu', row, event)"
      @row-dblclick="(row, event) => emit('row-dblclick', row, event)"
      @header-click="(column, event) => emit('header-click', column, event)"
      @header-contextmenu="(column, event) => emit('header-contextmenu', column, event)"
      @sort-change="args => emit('sort-change', args)"
      @filter-change="filters => emit('filter-change', filters)"
      @current-change="(currentRow, oldCurrentRow) => emit('current-change', currentRow, oldCurrentRow)"
      @header-dragend="(newWidth, oldWidth, column, event) => emit('header-dragend', newWidth, oldWidth, column, event)"
      @expand-change="(row, expandedRows) => emit('expand-change', row, expandedRows)">
      <slot name="prepend" />

      <slot>
        <el-table-column
          v-for="(col, index) in columns"
          v-if="col.type == undefined"
          :key="index"
          :column-key="col.columnKey"
          :label="col.label"
          :prop="col.prop"
          :width="col.width"
          :min-width="col.minWidth"
          :fixed="col.fixed"
          :render-header="col.renderHeader"
          :sortable="col.sortable"
          :sort-method="col.method"
          :sort-by="col.sortBy"
          :sort-orders="col.sortOrders"
          :resizable="col.resizable"
          :formatter="col.formatter"
          :show-overflow-tooltip="col.showOverflowTooltip"
          :align="col.align"
          :header-align="col.headerAlign || col.align"
          :class-name="col.className"
          :label-class-name="col.labelClassName"
          :selectable="col.selectable"
          :reserve-selection="col.reserveSelection"
          :filters="col.filters"
          :filter-placement="col.filterPlacement"
          :filter-multiple="col.filterMultiple"
          :filter-method="col.filterMethod"
          :filtered-value="col.filteredValue">
          <template slot-scope="scope">
            <span v-if="col.filter">
              {{ Vue.filter(col['filter'])(scope.row[col.prop]) }}
            </span>
            <span v-else-if="col.slotName">
              <slot :name="col.slotName" :row="scope.row" :$index="scope.$index" />
            </span>
            <span v-else-if="col.render">
              <VColumn :column="col" :row="scope.row" :render="col.render" :index="scope.$index" :size="size" />
            </span>
            <span v-else-if="col.formatter">
              {{ col.formatter(scope.row, scope.column, scope.row[col.prop], scope.$index) }}
            </span>
            <span v-else>
              {{ scope.row[col.prop] }}
            </span>
          </template>
        </el-table-column>

        <el-table-column v-else :key="index" v-bind="col" />
      </slot>

      <slot name="append" />
    </el-table>

    <el-pagination
      v-if="internalPagination.show"
      ref="elPagination"
      :layout="layout"
      :total="internalPagination.total"
      :small="internalPagination.isSmall"
      :page-sizes="pageSizes"
      :page-size="internalPagination.size"
      :current-page="internalPagination.index"
      background
      class="app-table-pagination"
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange" />
  </div>
</template>

<script>
/**
 * el 数据表格封装，依赖 axios
 * @params: elTable所有参数
 * @params: elPagintion所哟有参数
 */
import './table.scss'
import request from 'r'
import elProps from './el-props.js'
import { isEmptyObject, isFunction } from './utils'

const QUERY_MULTIPLE = 5 // 如果是查询10条数据，则需要翻 5 倍，即查询 50 条

// 后台需要的计算公式的变量：
// 开始位置: (page - 1) * offset
// 查询条数: limit
// 例如查第3页，每页展示10条，最大查 10 * QUERY_MULTIPLE = 50，那么后台开始从位置：(3-1)*10, 查询 50 条数据
const PAGE = { // 分页查询的关键字
  indexName: 'page', // 当前页
  offsetName: 'offset', // 查询条数
  sizeName: 'limit' // 推荐最大条数
}

export default {
  name: 'AppTable',
  componentName: 'AppTable',
  provide() {
    return {
      appTable: this
    }
  },
  components: {
    VColumn: {
      functional: true,
      props: {
        row: Object,
        render: Function,
        index: Number,
        column: {
          type: Object,
          default: null
        },
        size: String
      },
      render(h, ctx) {
        const params = {
          row: ctx.props.row,
          index: ctx.props.index,
          size: ctx.props.size
        }
        if (ctx.props.column) {
          params.column = ctx.props.column
        }
        return ctx.props.render(h, params)
      }
    }
  },
  props: {
    ...elProps,

    showPagination: {
      type: Boolean,
      default: true
    },
    stdPagination: Boolean,
    pageIndexName: {
      type: String,
      default: PAGE.indexName
    },
    pageSizeName: {
      type: String,
      default: PAGE.sizeName
    },
    pageOffsetName: {
      type: String,
      default: PAGE.offsetName
    },
    axios: {
      type: Object,
      default() {
        return {}
      }
    },
    axiosUrl: String,
    axiosMethod: {
      type: String,
      default: 'get',
      validator: value => ['get', 'post', 'put', 'delete'].includes(value.toLowerCase())
    },
    axiosHeaders: {
      type: Object,
      default() {
        return {}
      }
    },
    axiosParams: {
      type: Object,
      default() {
        return {}
      }
    },
    axiosParseData: {
      type: [Boolean, Function],
      default: false
    },
    columns: {
      type: Array,
      default: () => []
    },
    debug: Boolean
  },
  data() {
    return {
      internalTableData: [],
      internalRealTableData: [],
      internalEmptyText: this.emptyText,
      internalPagination: {
        show: this.showPagination,
        index: this.currentPage,
        size: this.pageSize,
        total: 0,
        isSmall: this.size === 'small' || this.size === 'mini'
      },
      MULS: QUERY_MULTIPLE
    }
  },
  computed: {
    internalFetchType() {
      return this.axiosUrl || this.axios.url ? 'remote' : 'local'
    }
  },
  watch: {
    data: {
      handler(value) {
        value && this.internalRealTableData.push(...value)
        this.loadLocalData()
      },
      immediate: true
    }
  },
  mounted() {
    this.handleQuery()
  },
  methods: {
    emit(emitName) {
      this.$emit(emitName, ...Array.from(arguments).slice(1))
    },
    handleSizeChange(pageSize) {
      this.internalPagination.size = pageSize
    },
    handleCurrentChange(pageCurrent) {
      this.internalPagination.index = pageCurrent
      this.internalFetchType === 'local' ? this.loadLocalData() : this.handleFetch()
    },
    handleQuery(resetPageIndex = true) {
      if (resetPageIndex) {
        this.internalPagination.index = 1
      }

      this.internalFetchType === 'local' ? this.loadLocalData() : this.handleFetch()
    },
    handleFetch() {
      this._getRequest().then(response => {
        this.emit('axios-resolve', response)
        let body = response.data
        if (isFunction(this.axios.parseData)) {
          body = this.axiosParseData(body)
        }
        if (isFunction(this.axiosParseData)) {
          body = this.axiosParseData(body)
        }
        this.debug && console && console.log('结果：', body)
        this.debug && console && console.groupEnd()

        const data = body.data
        const length = data && Array.isArray(data) ? data.length : -1

        if (length === 0) {
          this.internalTableData = []
          this.internalRealTableData = []
          this.internalEmptyText = body.message || this.emptyText
          this.internalPagination.show = false
        } else if (length === -1) {
          this.internalTableData = []
          this.internalRealTableData = []
          this.internalEmptyText = '不支持的数据格式'
          this.internalPagination.show = false
          console.error('当前数据：', data)
        } else {
          if (this.stdPagination) {
            this.internalTableData = data
            this.internalPagination.total = body.count || length
          } else {
            // 关于分页需要截取
            const { index, size } = this.internalPagination
            const _data = data.slice(0, size)
            this.internalPagination.total = length + (index - 1) * size
            if (this.showPagination) {
              this.internalPagination.show = true
            }
            this.internalTableData = _data
            this.internalRealTableData = data
          }
        }
        this.emit('axios-complete', response)
      }, error => {
        this.emit('axios-reject', error)
        this.internalTableData = []
        this.internalRealTableData = []
        this.internalEmptyText = error && error.message || '数据异常'
        this.internalPagination.show = false
        this.emit('axios-complete', error)
        this.debug && console && console.groupEnd()
      })
    },
    loadLocalData() {
      this.internalTableData = this.roamLocalData()
      if (this.showPagination) {
        this.internalPagination.show = this.internalTableData.length !== 0
      }
      this.internalPagination.total = this.internalRealTableData.length
    },
    roamLocalData() {
      const { index, size } = this.internalPagination
      const _data = this.internalRealTableData
      return this.showPagination ? _data.filter((v, i) => i >= (index - 1) * size && i < index * size) : _data
    },
    _getRequest() {
      const { url, method } = this.axios
      let { params, headers } = this.axios

      const options = {
        url: url || this.axiosUrl,
        method: method || this.axiosMethod
      }

      if (isEmptyObject(params)) {
        params = this.axiosParams
      }

      if (isEmptyObject(headers) && !isEmptyObject(this.axiosHeaders)) {
        headers = this.axiosHeaders
        options.headers = headers
      }

      if (this.showPagination) {
        this.stdPagination
          ? Object.assign(params, {
            [this.pageIndexName]: this.internalPagination.index,
            [this.pageSizeName]: this.internalPagination.size
          })
          : Object.assign(params, {
            [this.pageIndexName]: this.internalPagination.index, // 与后台数据库 sql 文同步，需要减去 1
            [this.pageSizeName]: this.internalPagination.size * QUERY_MULTIPLE,
            [this.pageOffsetName]: this.internalPagination.size
          })
      }

      const finalOptions = options.method === 'get' ? { params } : { data: params, headers: { 'Content-Type': 'application/json;charset=UTF-8' }}
      Object.assign(finalOptions, options)

      this.debug && console && console.group(`AppTable ${ new Date().toLocaleString ? new Date().toLocaleString() : '' }`)
      this.debug && console && console.log(`参数：`, finalOptions)
      return request(finalOptions)
    }
  }
}
</script>
